# l4t-extlinux-config.bbclass
#
# This class allow the extlinux.conf generation for use with
# the L4TLauncher UEFI boot application, which is derived from
# the extlinux.conf support in the older cboot bootloader on
# Tegra platforms.
#
# This is adapted from OE-Core's uboot-extlinux-config class,
# and uses the same variable names (with one addition), but
# has the following differences:
#
#  1. The addition of a MENU TITLE line, which MUST be present.
#  2. Labels are used for the LABEL lines. Menu descriptions
#     are used for MENU LABEL lines, which MUST be present.
#  3. A DEFAULT line is always included, even if there is only
#     one entry.
#  4. FDTDIR is not supported.
#  5. Relative path names are not permitted.
#  6. UBOOT_EXTLINUX_CONSOLE is not used, and
#     UBOOT_EXTLINUX_KERNEL_ARGS defaults to null string, due to
#     common boot args being inserted into the device tree.
#  7. An APPEND line *must* begin with "${cbootargs} " to be appended
#     to the kernel command line. (It is automatically prepended
#     in this bbclass.) ** NB: In L4T R35.2.1, a bug in the L4TLauncher
#     code also requires this line to be present.
#  8. L4TLauncher uses 'LINUX' instead of 'KERNEL' for the kernel image.
#
# External variables:
#   See uboot-extlinux-config.bbclass for the basic set.
# UBOOT_EXTLINUX_MENU_TITLE        - Menu title line
#
# Original copyright from uboot-extlinux-config.bblcass
# Copyright (C) 2016, O.S. Systems Software LTDA.  All Rights Reserved
# Released under the MIT license (see packages/COPYING)

UBOOT_EXTLINUX_LABELS ??= "primary"
UBOOT_EXTLINUX_FDT ??= ""
UBOOT_EXTLINUX_KERNEL_IMAGE ??= "/boot/${KERNEL_IMAGETYPE}"
UBOOT_EXTLINUX_KERNEL_ARGS ??= ""
UBOOT_EXTLINUX_MENU_DESCRIPTION_primary ??= "${DISTRO_NAME}"
UBOOT_EXTLINUX_MENU_TITLE ??= "L4T boot options"

UBOOT_EXTLINUX_CONFIG = "${B}/extlinux.conf"

python do_create_extlinux_config() {
    if d.getVar('UBOOT_EXTLINUX') != '1':
        return

    if not d.getVar('WORKDIR'):
        bb.error("WORKDIR not defined, unable to package")

    labels = (d.getVar('UBOOT_EXTLINUX_LABELS') or '').split()
    if len(labels) == 0:
        bb.fatal("UBOOT_EXTLINUX_LABELS not defined or null, nothing to do")

    cfile = d.getVar('UBOOT_EXTLINUX_CONFIG')
    if not cfile:
        bb.fatal('Unable to read UBOOT_EXTLINUX_CONFIG')

    localdata = bb.data.createCopy(d)

    cfg = '# L4TLauncher configuration file generated by OE4T\n'

    cfg += 'MENU TITLE %s\n' % d.getVar('UBOOT_EXTLINUX_MENU_TITLE')

    timeout =  localdata.getVar('UBOOT_EXTLINUX_TIMEOUT')
    if timeout:
        cfg += 'TIMEOUT %s\n' % timeout

    default = localdata.getVar('UBOOT_EXTLINUX_DEFAULT_LABEL') or labels[0]
    if default not in labels:
        bb.fatal('UBOOT_EXTLINUX_DEFAULT_LABEL (%s) not in UBOOT_EXTLINUX_LABELS' % default)
    cfg += 'DEFAULT ' + default + '\n'

    # Need to deconflict the labels with existing overrides
    default_overrides = localdata.getVar('OVERRIDES').split(':')

    # We're keeping all the existing overrides that aren't used as a label
    # an override for that label will be added back in while we're processing that label
    keep_overrides = list(filter(lambda x: x not in labels, default_overrides))

    for label in labels:
        localdata.setVar('OVERRIDES', ':'.join(keep_overrides + [label]))

        menu_description = localdata.getVar('UBOOT_EXTLINUX_MENU_DESCRIPTION')
        if not menu_description:
            menu_description = label

        kernel_image = localdata.getVar('UBOOT_EXTLINUX_KERNEL_IMAGE')

        fdt = localdata.getVar('UBOOT_EXTLINUX_FDT')
        if fdt:
            fdt = '\tFDT ' + fdt + '\n'

        cfg += 'LABEL %s\n\tMENU LABEL %s\n\tLINUX %s\n%s' % (label, menu_description, kernel_image, fdt)

        initrd = localdata.getVar('UBOOT_EXTLINUX_INITRD')
        if initrd:
            cfg += '\tINITRD ' + initrd + '\n'

        kernel_args = localdata.getVar('UBOOT_EXTLINUX_KERNEL_ARGS')
        if kernel_args:
            kernel_args = '${cbootargs} ' + kernel_args
            cfg += '\tAPPEND ' + kernel_args + '\n'
        else:
            # This line is required when using unpatched UEFI 35.2.1
            cfg += '\tAPPEND ${cbootargs}\n'

    # When signing, input length is expected to be an even multiple
    # of 16 bytes; otherwise the signing tool will pad it with 0x80,
    # which could cause it to be unparseable by ordinary mortals
    # (and their tools).  Pad it with linefeeds here to prevent that.
    cfgextra = len(cfg) % 16
    if cfgextra > 0:
        cfg += '\n' * (16 - cfgextra)

    try:
        with open(cfile, 'w') as cfgfile:
            cfgfile.write(cfg)
    except OSError:
        bb.fatal('Unable to open %s' % (cfile))
}
UBOOT_EXTLINUX_VARS = "CONSOLE MENU_DESCRIPTION KERNEL_IMAGE FDT KERNEL_ARGS INITRD"
do_create_extlinux_config[vardeps] += "${@' '.join(['UBOOT_EXTLINUX_%s_%s' % (v, l) for v in d.getVar('UBOOT_EXTLINUX_VARS').split() for l in d.getVar('UBOOT_EXTLINUX_LABELS').split()])}"
do_create_extlinux_config[vardepsexclude] += "OVERRIDES"

addtask create_extlinux_config before do_install do_deploy after do_compile
